/*
 Modified Socket code. Originally from 
 http://www.gnu.org/software/libtool/manual/libc/Server-Example.html#Server-Example
 
*/
#include "socket.h"

#define PORT    31337
#define MAXMSG  512
//extern void perror(char *str);


          #undef FD_SETSIZE
          #define FD_SETSIZE 10
          
void loghex(char *what, unsigned char *buf, int length);

CSocket::CSocket()
{
        perror("CSocket::CSocket()");

        buffers_count = 0;
        data_avail = 0;
        data_new = 0;
        
        /* Create the socket and set it up to accept connections. */
        sock = make_socket(PORT);
       
        printf("\tsocket: %d\n", sock);
        if (listen (sock, 1) < 0)
        {
           perror("\tlisten < 0");
           return;
        }


/*
        int flags;   
//Set socket to non-blocking 

        if ((flags = fcntl(sock, F_GETFL, 0)) < 0) 
        { 
            // Handle error  
            perror("sadfasf");
        } 
        
        if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) 
        { 
            //Handle error 
           perror("2.02.02.02.02.0sadfasf");
 
        } 
  */   

        
        /* Initialize the set of active sockets. */
        FD_ZERO (&active_fd_set);
        FD_SET (sock, &active_fd_set);
        
        return; 
}

CSocket::~CSocket()
{
     perror("~CSocket()");
     for (int i = 0; i < FD_SETSIZE; ++i)
     {
         close (i);
         FD_CLR (i, &active_fd_set);
     }
     
     for(int i = 0; i < buffers.size(); i++)
     {
             delete[](buffers[i]->buffer);
             delete(buffers[i]);
     }
     perror("~CSocket() completed");
     
     return;
}

int CSocket::make_socket(uint16_t port)
{
   // int sock;
    //struct sockaddr_in name;
    
    perror ("CSocket::make_socket()");
    
    /* Create the socket. */
    sock = socket(PF_INET, SOCK_STREAM, 0);
    
    if (sock < 0)
    {
       perror ("\tsock < 0");
       return -1;
    }
    
    /* Give the socket a name. */
    name.sin_family = AF_INET;
    name.sin_port = htons (port);
    name.sin_addr.s_addr = htonl (INADDR_ANY);
    if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
    {
       perror ("\tbind < 0");
       return -1;
    }
    
    return sock;
}
     
int CSocket::read_from_client(int filedes)
{
    unsigned char *buffer = new unsigned char[MAXMSG];   
    
  if(verbose_log)
    perror("CSocket::rfc()");
    
    int nbytes;
    if(buffer == NULL) 
    {
          perror("\tnew buffer error");
          return -1;    
    }
    
    nbytes = read(filedes, buffer, MAXMSG);
    
    if (nbytes < 0) {      // Read error
            if(buffer != NULL) delete[](buffer);
            
            perror ("\tCSocket::rfc() read error");
            return -1;

     } else if (nbytes == 0){// End-of-file.
            if(buffer != NULL) delete[](buffer);
            perror ("\teof nbytes == 0");
            return -1;
     }  else {
            //perror ("\tdata avail");
            //Data read.
            data_avail = true;
            data_new = true;
            
            buffers_count++;
            
            //write_test(filedes);
            
            BUFFER_STRUCT *bstruct =  new BUFFER_STRUCT;
            if(bstruct == NULL) 
            {
               perror("\tbstruct new[] failed");
               return -1;
            }
            
            bstruct->buffer = new unsigned char[MAXMSG];
            
            if(bstruct->buffer == NULL)
            {
               perror("\tbstruct buffer new[] failed");
               return -1;
            }
            
            bstruct->buffer = buffer;
            bstruct->len = nbytes;
            
            if(buffers.size() == 100) buffers.clear(); //store only 100 packets
            
            
            buffers.push_back(bstruct);
           
           if(verbose_log)
            printf("CSocket::rfc(): Added bstruct(%d) len=%d\n", buffers_count, nbytes);
            // fprintf (stderr, "Server: got message: `%s'\n", buffer);
            if(verbose_log)
                 loghex("CSocket::rfc() readfrom", buffer, nbytes);
            // parse(buffer, nbytes);
          
     }
     
     return nbytes;
}

void CSocket::init(void)
{
}


int CSocket::process(void)
{
        int i;
        int ret = 0;
        struct sockaddr_in clientname;
        size_t size;

if(verbose_log) 
        perror("CSocket::process()");  
          //TODO: I DO NOT want to block
          
           /* Block until input arrives on one or more active sockets. */
           read_fd_set = active_fd_set;
           /* TODO (#1#): timeout for select */
             timeout.tv_sec = 0;
		     timeout.tv_usec = 500000;
		    
            ret = select (FD_SETSIZE, &read_fd_set, NULL, NULL, &timeout);
           if (ret < 0)
           {
               perror ("select < 0");
               return -1;
           }
            
          // printf("\tFD_SETSIZE %d\n", FD_SETSIZE);
        
           /* Service all the sockets with input pending. */

        //  #define FD_SETSIZE0 5
          // #warning FD_SETSIZE
           for (i = 0; i < FD_SETSIZE; ++i)
             if (FD_ISSET (i, &read_fd_set))
             {
                 if (i == sock)
                 {
                         // Connection request on original socket. 
                         perror("\tconnection request");
                         
                         size = sizeof (clientname);
                        // #ifndef __CYGWIN__
                                 newfd = accept (sock, (struct sockaddr *) &clientname, (socklen_t*)&size);
                        // #else
                        //         newfd = accept (sock, (struct sockaddr *) &clientname,&size);
                        // #endif
                        // perror("bam");
                         
                         if (newfd < 0)
                         {
                             perror ("\taccept < 0");
                             return -1;
                         }
                         //perror ("fprintf");
                         
                         fprintf (stderr,
                                  "\tServer: connect from host %s, remote? port %hd.\n",
                                  inet_ntoa(clientname.sin_addr),
                                  ntohs(clientname.sin_port));
                         
                         printf("\tFDSET0\n");
                         FD_SET (newfd, &active_fd_set);
                         printf("\tFDSET1\n");

                   } else {  // Data arriving on an already-connected socket. 
                       
                         ret = read_from_client(i);
                         
                         if (ret < 0)
                         {
                             perror("\tret < 0");
                             FD_CLR (i, &active_fd_set);
                             close (i);
                         } else {
                           // break;    
                         }
                 }
             }
      //  read_fd_set = active_fd_set;

        return ret;
}

// No description
void CSocket::write_test(int sock)
{
    perror("CSocket::write_test()");
    
    struct dude
    {
           int omg;
           char wtf;
           char bbq[32];
    } ohyes;
    
    ohyes.omg = 10;
    ohyes.wtf = 'f';
    //ohyes.bbq = new char[32];
    perror("\tnew char");
    //ohyes.bbq[0] = 'g';
    sprintf(ohyes.bbq, "this is not a test");
    
    //ohyes.bbq = "this is not a test\0";
    //write(x, sizeof(x[0]), sizeof(x)/sizeof(x[0]), fp);
  //  fwrite(&ohyes, sizeof(ohyes), 1, fp);
     
     
     char *packet = "root access denied\n";
     printf("\tsizeof(packet)=%d, sizeof(ohyes)=%d\n", sizeof(packet), sizeof(ohyes));
     
     for (int i = 0; i < FD_SETSIZE; ++i)
     {
        // send(i, (char*)packet, strlen(packet), 0);
        if (FD_ISSET (i, &read_fd_set))
        {
          send(i, &ohyes, sizeof(ohyes), 0);
        }
     }
     
     //delete [](ohyes.bbq);
     
     
}

void loghex(char *what, unsigned char *buf, int length)
{
	printf("%s: len: %d: ", what, length);

	if(buf != NULL) 
	{
      for(int x = 0; x < length; x++) printf("%02X ", buf[x]);
    }
    
	printf("\n");
}

unsigned char* CSocket::getData(unsigned char* &buffer, int &len)
{
     //perror("CSocket::getdata()");
    // printf("buffer size %d\n", buffers.size());
     
     if(buffers.size() <= 0) 
     {
        perror("\tbuffers vector null");
        
        len = 0;
        buffer = NULL;
        
        return NULL;
     }
 //    if(&buffer
     buffer = buffers[buffers.size()-1]->buffer;
     len = buffers[buffers.size()-1]->len;
    
     data_new = false;
    // printf("len: %d\n", len);
  //   perror("\tfinished");
     return NULL;    
}

int CSocket::sendData(unsigned char *buffer, int len)
{
   if(verbose_log)
   {
    perror("CSocket::sendData");
    printf("\tData length: %d\n", len);
   }
   
   if(buffer == NULL)
   {
             perror("\tbuffer == null"); 
             return -1;
   } 
   

   for (int i = 0; i < FD_SETSIZE; ++i)
   {
        // send(i, (char*)packet, strlen(packet), 0);
        
        if(i == sock) {//VERY IMPORTANT!
             //perror("fuck it");
             
             continue;
        }
        
        if (FD_ISSET (i, &active_fd_set))
        {
         // printf("send(%d)\n", i);
          send(i, buffer, len, 0);
        }
   }

//   send(newfd, (char*)buffer, len, 0 );
   
    
   return 0;
}

bool CSocket::newDataCheck()
{
     return data_new;
}
